﻿using System;
using System.Collections.Generic;
using System.Linq;
using Outlook = Microsoft.Office.Interop.Outlook;
using System.Data;
using System.Xml;
using System.Data.SqlServerCe;
using System.Diagnostics;


namespace Investafinance.Sync
{
    //Класс производит синхронизацию одной таблици БД с 1м листом SharePoint
    internal class SyncSQLServer<TItem> where TItem : class
    {
        
        // строка конекта к бызе данных
        private string scon;// = @"Data Source=C:\Meta.sdf";
        private string tableNameInOutlook = "Collaboratore";

        public SyncSQLServer(string sConnectionString)
        {
            this.scon = sConnectionString;
        }

        //в этом классе есть события которые надо обрабатывать по разнаму для каждой таблици 

        #region оперделение делигатов событий
        public event EventHandler<EventGetNewFildEventArgs> EventGetNewFild;                                            //1
        public event EventHandler<EventCopyTableEventArgs> EventCopyTable;                                              //2
        public event EventHandler<EventRemuveFildEventArgs> EventRemuveFild;                                            //3
        public event EventHandler<EventStartKolisy_3_EventArgs> EventStartKolisy_3;                                     //4
        public event EventHandler EventDoIzmeneniaClientaKolisy_3;                                                      //5
        public event EventHandler EventDoIzmeneniaServerKolisy_3;                                                       //6
        public event EventHandler<EventStartKolisy_2_EventArgs> EventStartKolisy_2;                                     //7
        public event EventHandler<EventPosleIzmeneniaServerKolisy_2_EventArgs> EventPosleIzmeneniaServerKolisy_2;       //8
        public event EventHandler<EventPosleIzmeneniaClientaKolisy_2_EventArgs> EventPosleIzmeneniaClientaKolisy_2;     //9
        public event EventHandler<EventStartKolisy_1_EventArgs> EventStartKolisy_1;                                     //10
        public event EventHandler<EventPosleIzmeneniaClientaKolisy_1_EventArgs> EventPosleIzmeneniaClientaKolisy_1;     //11
        public event EventHandler EventEndKolizii_1;                                                                    //12
        public event EventHandler EventDoIzmeneniaClientaKolisy_1;                                                      //13
        public event EventHandler<EventLoadUpdateEventArgs> EventLoadUpdate;                                            //14
        public event EventHandler<EventOutlookUpdateEventArgs> EventOutlookUpdate;                                      //15
        public event EventHandler EventCreateUpdate;                                                                    //16
        public event EventHandler<ProcentEventArgs> EventProcent;                                                       //17
        public event EventHandler<UpdateIDInOutlookEventArgs> EventUpdateIDInOutlook;                                   //18
        #endregion

        #region методы отвецтвенные за уведомление событий

        protected virtual void OnEventProcent(ProcentEventArgs e)
        {
            EventHandler<ProcentEventArgs> temp = EventProcent;
            if (temp != null) temp(this, e);
        }

        protected virtual void OnEventUpdateIDInOutlook(UpdateIDInOutlookEventArgs e)
        {
            EventHandler<UpdateIDInOutlookEventArgs> temp = EventUpdateIDInOutlook;
            if (temp != null) temp(this, e);
        }


        protected virtual void OnEventRemuveFild(EventRemuveFildEventArgs e)
        {
            EventHandler<EventRemuveFildEventArgs> temp = EventRemuveFild;
            if (temp != null) temp(this, e);
        }
        protected virtual void OnEventGetNewFild(EventGetNewFildEventArgs e)
        {
            EventHandler<EventGetNewFildEventArgs> temp = EventGetNewFild;
            if (temp != null) temp(this, e);
        }
        protected virtual void OnEventCopyTable(EventCopyTableEventArgs e)
        {
            EventHandler<EventCopyTableEventArgs> temp = EventCopyTable;
            if (temp != null) temp(this, e);
        }
        protected virtual void OnEventStartKolisy_3(EventStartKolisy_3_EventArgs e)
        {
            EventHandler<EventStartKolisy_3_EventArgs> temp = EventStartKolisy_3;
            if (temp != null) temp(this, e);
        }
        protected virtual void OnEventDoIzmeneniaClientaKolisy_3()
        {
             EventDoIzmeneniaClientaKolisy_3(this, EventArgs.Empty);
        }
        protected virtual void OnEventDoIzmeneniaServerKolisy_3()
        {
             EventDoIzmeneniaServerKolisy_3(this, EventArgs.Empty);
        }
        protected virtual void OnEventStartKolisy_2(EventStartKolisy_2_EventArgs e)
        {
            EventHandler<EventStartKolisy_2_EventArgs> temp = EventStartKolisy_2;
            if (temp != null) temp(this, e);
        }
        protected virtual void OnEventPosleIzmeneniaServerKolisy_2(EventPosleIzmeneniaServerKolisy_2_EventArgs e)
        {
            EventHandler<EventPosleIzmeneniaServerKolisy_2_EventArgs> temp = EventPosleIzmeneniaServerKolisy_2;
            if (temp != null) temp(this, e);
        }
        protected virtual void OnEventPosleIzmeneniaClientaKolisy_2(EventPosleIzmeneniaClientaKolisy_2_EventArgs e)
        {
            EventHandler<EventPosleIzmeneniaClientaKolisy_2_EventArgs> temp = EventPosleIzmeneniaClientaKolisy_2;
            if (temp != null) temp(this, e);
        }
        protected virtual void OnEventStartKolisy_1(EventStartKolisy_1_EventArgs e)
        {
            EventHandler<EventStartKolisy_1_EventArgs> temp = EventStartKolisy_1;
            if (temp != null) temp(this, e);
        }
        protected virtual void OnEventPosleIzmeneniaClientaKolisy_1(EventPosleIzmeneniaClientaKolisy_1_EventArgs e)
        {
            EventHandler<EventPosleIzmeneniaClientaKolisy_1_EventArgs> temp = EventPosleIzmeneniaClientaKolisy_1;
            if (temp != null) temp(this, e);
        }
        protected virtual void OnEventEndKolizii_1()
        {
            EventEndKolizii_1(this, EventArgs.Empty);
        }
        protected virtual void OnEventDoIzmeneniaClientaKolisy_1()
        {
            EventDoIzmeneniaClientaKolisy_1(this, EventArgs.Empty);
        }
        protected virtual void OnEventLoadUpdate(EventLoadUpdateEventArgs e)
        {
            EventHandler<EventLoadUpdateEventArgs> temp = EventLoadUpdate;
            if (temp != null) temp(this, e);
        }
        protected virtual void OnEventOutlookUpdate(EventOutlookUpdateEventArgs e)
        {
            EventHandler<EventOutlookUpdateEventArgs> temp = EventOutlookUpdate;
            if (temp != null) temp(this, e);
        }
        protected virtual void OnEventCreateUpdate()
        {
            EventCreateUpdate(this, EventArgs.Empty);
        }
        #endregion

        //Процент выполнения синхронизации
        private int _Procent;
        public int Procent
        {
            get 
            {
                    return _Procent; 
            }
            set
            {
                _Procent = value;
                //Тут дёргним делигат
                OnEventProcent(new ProcentEventArgs(value));
            }
        }

        //метод делает строку UPDATE SQL для клиента 
        private string GetUpdateStringClient(DataRow RowIn, SyncObject syncTable)
        {
            string s = "UPDATE " + syncTable.table + " SET ";
            for (int i = 0; i < Convert.ToInt32(syncTable.pola.Length / 2); i++)
            {
                if (i != Convert.ToInt32(syncTable.pola.Length / 2) - 1)
                {
                    if (syncTable.pola[i, 1] == "string")
                        s = s + syncTable.pola[i, 0] + " = '" + RowIn[syncTable.pola[i, 0]].ToString().Trim() + "', ";
                    else
                        if (syncTable.pola[i, 1] == "int")
                            s = s + syncTable.pola[i, 0] + " =" + RowIn[syncTable.pola[i, 0]].ToString().Trim() + " , ";
                }
                else
                {
                    if (syncTable.pola[i, 1] == "string")
                        s = s + syncTable.pola[i, 0] + " = '" + RowIn[syncTable.pola[i, 0]].ToString().Trim() + "' ";
                    else
                        if (syncTable.pola[i, 1] == "int")
                            s = s + syncTable.pola[i, 0] + " = " + RowIn[syncTable.pola[i, 0]].ToString().Trim() + " ";
                }
            }
            return s;
        }

        private int metodId = 0; 
        //метод делает строку UPDATE SQL для ServerA
        private string GetUpdateStringServer(SqlCeDataReader RowIn, SyncObject syncTable)
        {
            metodId++;
            string s = "<Method ID='" + metodId.ToString() + "' Cmd='Update'>";
            s+="<Field Name='" + syncTable.polaKEY[0, 0] + "'>" +  RowIn["ID"].ToString() + "</Field>";
            for (int i = 0; i < Convert.ToInt32(syncTable.pola.Length / 2); i++)
            {
                 string strFildName;
                 string strFildValue;

                 if (syncTable.pola[i, 0] != "owshiddenversion")
                 {
                     strFildName = syncTable.pola[i, 0];
                     strFildValue = RowIn[syncTable.pola[i, 0]].ToString().Trim().Replace(">", "&gt;");

                     strFildValue = strFildValue.Replace("<", "&lt;");
                     s += "<Field Name='" + strFildName + "'>" + strFildValue + "</Field>";
                 }
             }
            s += "</Method>";
            return s;
        }

        //метод делает строку Insert SQL для ServerA
        private List<string []> listID = new List<string[]>();

        public string GetInsertStringServer(SyncObject syncTable, SqlCeDataReader RowIn)
        {
            metodId++;
            string [] prefix = new string[2];
            prefix[0]=metodId.ToString();
            prefix[1]=RowIn[syncTable.polaKEY[0,0]].ToString();
            listID.Add(prefix);

            string s = "<Method ID='" + metodId.ToString() + "' Cmd='New'>";
            s += "<Field Name='ID'>New</Field>";

            for (int i = 0; i < Convert.ToInt32(syncTable.pola.Length / 2); i++)
            {
                string strFildName = syncTable.pola[i, 0];
                string strFildValue = RowIn[syncTable.pola[i, 0]].ToString().Trim().Replace(">", "&gt;");
                strFildValue = strFildValue.Replace("<", "&lt;");
                s += "<Field Name='" + strFildName + "'>" + strFildValue + "</Field>";
            }
            s += "</Method>";
            return s;
        }

//это изменения сервера
//изменения клиента нам ненужны так как до начала синхронизации все они и так будут на месте
private List<string> UpdateID;
private List<string> DeleteID;
private List<string> InsertID;

//Метод для получения навых записей в таблице
public bool UpdateTable(Outlook.MAPIFolder FolderOutlook, SyncObject syncTable, string collectionFieldInSharepoint, string listName, Credential user)
{
    //папка называется также как и префикс для таблиц
    tableNameInOutlook = listName;
    //syncTable.table = listName;
    //логи изменения на случай exeption
    UpdateID = new List<string>();
    DeleteID = new List<string>();
    InsertID = new List<string>();

    Procent = 0;

    bool ret = true;

    //загрузим из access файла 
    SqlCeConnection conn = new SqlCeConnection(scon);
    try
    {

        //открываем подключение 
        conn.Open();
        SqlCeCommand cmd;

        try
        {
            Procent += 10;

            cmd = new SqlCeCommand("select Url from Folders where EntryID='" + FolderOutlook.EntryID + "'" , conn);
            SqlCeDataReader dr = cmd.ExecuteReader();
            string listURlVar="";
            if (dr.Read())
            {
                listURlVar = dr["Url"].ToString();
            }
            dr.Close();
            cmd.Dispose();
            cmd = null;

            Connector action = new Connector(collectionFieldInSharepoint , scon);
            action.GetDataSetFromSharePoint(listURlVar, listName,  user);

            //итак на случай если синхронизация выполняется в первый раз необходимо обновить набор полей
            ((Shell.ShellSynchronizer<TItem>)syncTable).CollectionField.Update();
            ((Shell.ShellSynchronizer<TItem>)syncTable).UpdateFolder();

            if (action.ds != null)
                if (action.IsUpdatingDataSet)
                {
                    string updateS = "";
                    string insertS = "";
                    string deleteS = "";

                    DataSet ds = action.ds;


                    //Получим записи подазрительные на колизии
                    cmd = new SqlCeCommand("select ID from " + syncTable.table + " where Modify in ('U','D')", conn);
                    dr = cmd.ExecuteReader();
                    cmd.Dispose();
                    cmd = null;
                    //Сформируем строку состоящею из EntryId подазрительных записей 
                    string WarningId;
                    if (dr.Read())
                    {
                        WarningId = "(";
                        WarningId += "'" + dr["ID"].ToString().Trim() + "'";
                        while (dr.Read())
                        {
                            WarningId += ",'" + dr["ID"].ToString().Trim() + "'";
                        }
                        WarningId += ")";
                    }
                    else
                    {
                        WarningId = "('-1')";
                    }
                    Procent += 10;

                    //Выделим колизии и новые поступления (новые поступления запишем )
                    DataRow[] Kolisi = null;


                    //Теперь надо Узнать какие записи мы будим обновлять а кокие прорсто сливать с сервера
                    cmd = new SqlCeCommand("select ID from " + syncTable.table +
                        " where Modify not in ('U','D','I')", conn);
                    dr = cmd.ExecuteReader();
                    cmd.Dispose();
                    cmd = null;
                    //Сформируем строку состоящею из EntryId подазрительных записей 
                    string WarningId2 = "";
                    if (dr.Read())
                    {
                        WarningId2 = "(";
                        WarningId2 += "'" + dr["ID"].ToString().Trim() + "'";
                        while (dr.Read())
                        {
                            WarningId2 += ",'" + dr["ID"].ToString().Trim() + "'";
                        }
                        WarningId2 += ")";
                    }
                    else
                    {
                        WarningId2 = "('-1')";
                    }
                    Procent += 10;

                    //возьмём все записи которые у клиента мы не изменяли но изменялись на сервере их надо обновить
                    DataRow[] Updating = ds.Tables["Table"].Select("ID in " + WarningId2);
                    //обновим записи
                    if ((Updating.Count()) > 0)
                    {
                        string sID = "";
                        OnEventCreateUpdate();
                        foreach (DataRow drow in Updating)
                        {
                            sID = drow["ID"].ToString().Trim();
                            OnEventLoadUpdate(new EventLoadUpdateEventArgs(drow));
                            //производим замену у клиента в access
                            cmd = new SqlCeCommand(GetUpdateStringClient(drow, syncTable)
                                                    + ", Modify ='' "
                                                    + " WHERE ID ='"
                                                    + sID + "'", conn);
                            cmd.ExecuteNonQuery();
                            cmd.Dispose();
                            cmd = null;
                            //запишем теперь изменения в outlook
                            OnEventOutlookUpdate(new EventOutlookUpdateEventArgs(drow));

                            //запишем в лог изменений ID  "на случай если упадём в exeption чтобы восстановить всё"
                            UpdateID.Add(sID);
                        }
                    }
                    Procent += 10;

                    //возмём все записи которые у клиента не обновлял  и не удалял ,and не те с котрыми клиент ничего не делал но уже скачал с сервера
                    //они и будут I
                    DataRow[] Insering = ds.Tables["Table"].Select("(ID not in " + WarningId + ")and(ID not in " + WarningId2 + ") ");
                    //-Записываем новые данные В клиента
                    OnEventGetNewFild(new EventGetNewFildEventArgs(Insering, FolderOutlook));

                    //запишем в лог изменений ID  "на случай если упадём в exeption чтобы восстановить всё"    
                    foreach (DataRow drow in Insering)
                        InsertID.Add(drow["ID"].ToString().Trim());

                    //-Теперь обработаем колизии 
                    //Обработаем U
                    cmd = new SqlCeCommand("select * from " + syncTable.table + " where Modify in ('U')", conn);
                    dr = cmd.ExecuteReader();
                    cmd.Dispose();
                    cmd = null;
                    while (dr.Read())
                    {
                        //поидеи тут одна запись
                        Kolisi = ds.Tables["Table"].Select("ID in ('" + dr["ID"] + "')");

                        DataRow[] Kolisi2 = ds.Tables["Table"].Select("ID in ('" + dr["ID"] + "')");

                        //Если изменение было на сервере до последннего изменения 
                        //то возникает колизия предлогаем пользователю выбрать внести свои  изменения 
                        //или оставить изменения которые на сервере
                        if (Kolisi.Count() != 0)
                        {
                            //-вопервых нужно показать окно вакансии
                            OnEventStartKolisy_1(new EventStartKolisy_1_EventArgs(dr));


                            //Выводим диалоговое окно
                            Forms.ModalConflict dealog = new Forms.ModalConflict();

                            dealog.Errorlabel.Text = "Значения этой формы были изменены другим пользователем.\nНажмите:\nДа - чтобы внести свои изменения на сервере.\n Нет - чтобы  заменить свои изменения этой формы в Outlook значениями другого пользователя.";


                            if (dealog.ShowDialog() != System.Windows.Forms.DialogResult.OK)
                            {
                                string sID = Kolisi[0]["ID"].ToString().Trim();

                                OnEventDoIzmeneniaClientaKolisy_1();

                                //производим замену у клиента в access
                                cmd = new SqlCeCommand(GetUpdateStringClient(Kolisi[0], syncTable)
                                                   + ", Modify = '' "
                                                   + " WHERE ID = '"
                                                   + sID + "'", conn);
                                cmd.ExecuteNonQuery();
                                cmd.Dispose();
                                cmd = null;

                                //запишем теперь изменения в outlook
                                OnEventPosleIzmeneniaClientaKolisy_1(new EventPosleIzmeneniaClientaKolisy_1_EventArgs(Kolisi[0]));
                                //запишем в лог изменений ID  "на случай если упадём в exeption чтобы восстановить всё"    
                                UpdateID.Add(sID);
                            }
                            else
                            {
                                //производим замену у сервера
                                updateS += GetUpdateStringServer(dr, syncTable);
                            }
                            OnEventEndKolizii_1();
                        }
                    }
                    Procent += 10;

                    //Обработаем D
                    cmd = new SqlCeCommand("select ID from " + syncTable.table + " where Modify in ('D')", conn);
                    dr = cmd.ExecuteReader();
                    cmd.Dispose();
                    cmd = null;
                    while (dr.Read())
                    {
                        //поидеи тут одна запись
                        Kolisi = ds.Tables["Table"].Select("ID in ('" + dr["ID"] + "')");

                        //Если удалена было в клиенте а на сервере были новые изменения этой записи то возникает колизия предлогаем пользователю выбрать внести свои  изменения или оставить изменения которые на сервере
                        if (Kolisi.Count() != 0)
                        {
                            //-вопервых нужно показать окно вакансии
                            OnEventStartKolisy_2(new EventStartKolisy_2_EventArgs(dr, FolderOutlook));

                            //Выводим диалоговое окно
                             Forms.ModalConflict dealog = new Forms.ModalConflict();
                             dealog.Errorlabel.Text = "Другой пользователь изменял значения этой формы после последней синхронизации.\nНажмите:\nДа - чтобы удалить изменения другого пользователя на сервере.\nНет – чтобы оставить изменения другого пользователя и восстановить их в Outlook.";
                            if (dealog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                            {
                                //если да то удаляем у сервера  
                                deleteS += "<Method ID='1' Cmd='Delete'>" +
                                                "<Field Name='ID'>" + dr["ID"].ToString() + "</Field>" +
                                           "</Method>";
                                OnEventPosleIzmeneniaServerKolisy_2(new EventPosleIzmeneniaServerKolisy_2_EventArgs(null));
                            }
                            else
                            {
                                //если нет то создаём новую вакансию
                                //для начала надо удалить запись о старой Item
                                cmd = new SqlCeCommand("DELETE FROM  " + syncTable.table + " where ID='" + dr["ID"].ToString() + "';", conn);
                                cmd.ExecuteNonQuery();
                                cmd.Dispose();
                                cmd = null;
                                //-Записываем новые данные В клиента
                                OnEventPosleIzmeneniaClientaKolisy_2(new EventPosleIzmeneniaClientaKolisy_2_EventArgs(Kolisi, FolderOutlook));
                                //запишем в лог изменений ID  "на случай если упадём в exeption чтобы восстановить всё"    
                                InsertID.Add(Kolisi[0]["ID"].ToString());
                            }
                        }
                    }
                    Procent += 10;

                    int g = 0;
                    //-после всего надо внести свои изменения серверу U,I,D
                    //сделаем U 
                    //тут тоже может возникать колизия (мы изменяем а ктото удалил)
                    cmd = new SqlCeCommand("select * from " + syncTable.table + " where Modify in ('U')", conn);
                    dr = cmd.ExecuteReader();
                    cmd.Dispose();
                    cmd = null;
                    while (dr.Read())
                    {
                        g += 1;
                        XmlDocument doc = new XmlDocument();
                        XmlElement batchElement = doc.CreateElement("Batch");
                        batchElement.SetAttribute("OnError", "Continue");
                        batchElement.SetAttribute("ListVersion", "1");
                        batchElement.SetAttribute("ViewName", action.viewHash);
                        batchElement.InnerXml = GetUpdateStringServer(dr, syncTable);
                        /*>>>*/
                        XmlNode rezalt = action.client.UpdateListItems(action.listNameHash, batchElement);
                        int i = ((XmlElement)rezalt).GetElementsByTagName("ErrorText").Count;

                        //Если сервер вернул ошибку то колизия запись уже ктото удалил 
                        if (i != 0)
                        {
                            OnEventStartKolisy_3(new EventStartKolisy_3_EventArgs(dr));

                            //Выводим диалоговое окно
                             Forms.ModalConflict dealog = new Forms.ModalConflict();
                             dealog.Errorlabel.Text = "Другой пользователь удалил запись о значениях этой формы  после последней синхронизации. \nНажмите:\nДа - чтобы восстановить запись о значениях этой формы  на сервере.\nНет – чтобы удалить в Outlook запись о значениях этой формы.";
                            if (dealog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                            {
                                OnEventDoIzmeneniaServerKolisy_3();
                                //если да то вносим запись в сервер
                                insertS += GetInsertStringServer(syncTable, dr);
                            }
                            else
                            {
                                string sID = dr["ID"].ToString();
                                OnEventDoIzmeneniaClientaKolisy_3();
                                //если нет то удаляем у клиента
                                cmd = new SqlCeCommand("DELETE FROM  " + syncTable.table + " where ID='" + sID + "';", conn);
                                cmd.ExecuteNonQuery();
                                cmd.Dispose();
                                cmd = null;
                                //запишем в лог изменений ID  "на случай если упадём в exeption чтобы восстановить всё"    
                                DeleteID.Add(sID);
                            }
                            if (g <= 11)
                                Procent += 1;
                        }
                    }

                    Procent = 70;

                    //сделаем I
                    cmd = new SqlCeCommand("select * from " + syncTable.table + " where Modify in ('I')", conn);
                    dr = cmd.ExecuteReader();
                    cmd.Dispose();
                    cmd = null;

                    string insertstr = "";
                    while (dr.Read())
                    {
                        insertstr += GetInsertStringServer(syncTable, dr);
                    }
                    insertS += insertstr;
                    Procent += 5;


                    //сделаем D
                    string delete = "";
                    cmd = new SqlCeCommand("select ID from " + syncTable.table + " where Modify in ('D')", conn);
                    dr = cmd.ExecuteReader();
                    cmd.Dispose();
                    cmd = null;

                    while (dr.Read())
                    {
                        metodId++;
                        delete += "<Method ID='" + metodId.ToString() + "' Cmd='Delete'>";
                        delete += "<Field Name='ID'>" + dr["ID"].ToString() + "</Field>";
                        delete += "</Method>";
                    }


                    deleteS += delete;
                    Procent += 5;

                    if (deleteS != "" || updateS != "")
                    {
                        XmlDocument doc = new XmlDocument();
                        XmlElement batchElement = doc.CreateElement("Batch");
                        batchElement.SetAttribute("OnError", "Continue");
                        batchElement.SetAttribute("ListVersion", "1");
                        batchElement.SetAttribute("ViewName", action.viewHash);
                        batchElement.InnerXml = deleteS + updateS;
                        //Тут Можно упасть в exeption если сервер не ответит
                        /*>>>*/
                        action.client.UpdateListItems(action.listNameHash, batchElement);
                    }
                    if (insertS != "")
                    {
                        XmlDocument doc = new XmlDocument();
                        XmlElement batchElement = doc.CreateElement("Batch");
                        batchElement.SetAttribute("OnError", "Continue");
                        batchElement.SetAttribute("ListVersion", "1");
                        batchElement.SetAttribute("ViewName", action.viewHash);
                        batchElement.InnerXml = insertS;
                        //Тут Можно упасть в exeption если сервер не ответит
                        /*>>>*/
                        XmlNode rezalt = action.client.UpdateListItems(action.listNameHash, batchElement);
                        //тут надо назначить правильные ID которые вернул сервер
                        XmlNodeList nL = ((XmlElement)rezalt).GetElementsByTagName("Result");
                        foreach (XmlNode Node in nL)
                        {
                            foreach (XmlAttribute atribut in Node.Attributes)
                            {
                                if (atribut.Name == "ID")
                                {
                                    XmlNodeList nL2 = ((XmlElement)Node).GetElementsByTagName("z:row");
                                    foreach (XmlNode Node2 in nL2)
                                    {
                                        foreach (XmlAttribute atribut2 in Node2.Attributes)
                                        {
                                            if (atribut2.Name == "ows_ID")
                                            {
                                                for (int i = 0; i < listID.Count; i++)
                                                {
                                                    if (listID[i][0] + ",New" == atribut.Value)
                                                    {
                                                        OnEventUpdateIDInOutlook(new UpdateIDInOutlookEventArgs(atribut2.Value, listID[i][1]));

                                                        cmd = new SqlCeCommand("UPDATE " + syncTable.table +
                                                            " SET ID='" + atribut2.Value +
                                                            "' where ID='" + listID[i][1] + "';", conn);
                 
                                                        cmd.ExecuteNonQuery();
                                                        cmd.Dispose();
                                                        cmd = null;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }

                    //Удалим в клиенской таблице лишние записи in ('D')
                    cmd = new SqlCeCommand("DELETE FROM  " + syncTable.table + " where Modify in ('D')", conn);
                    cmd.ExecuteNonQuery();
                    cmd.Dispose();
                    cmd = null;

                    //-Потом получить поля которые надо удалить у клиента и удалим их   <~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~<~
                    //для начала надо узнать а если вообще такие записи для этого сравним количиство записей на сервере и у клиента
                    cmd = new SqlCeCommand("select count(ID) from " + syncTable.table + "", conn);
                    dr = cmd.ExecuteReader();
                    cmd.Dispose();
                    cmd = null;

                    if (dr.Read())
                    {
                        int countKlient = Convert.ToInt32(dr[0]);
                        XmlDocument docViev = new XmlDocument();
                        docViev.LoadXml("<ViewFields><FieldRef Name='ID' /></ViewFields>");
                        //Тут Можно упасть в exeption если сервер не ответит
                        /*>>>*/
                        XmlNode p2 = action.client.GetListItems(action.listNameHash, action.viewHash, null, docViev, "100000", null, null);
                        XmlNodeList nL = ((XmlElement)p2).GetElementsByTagName("z:row");

                        int countServer = nL.Count;


                        if (countKlient > countServer)
                        {
                            //теперь надо найти ключи тех записей которые лишние
                            WarningId = "('-1'";
                            foreach (XmlNode Node in nL)
                            {
                                foreach (XmlAttribute atribut in Node.Attributes)
                                {
                                    if (atribut.Name == "ows_ID")
                                    {
                                        WarningId += ",'" + atribut.Value.ToString() + "'";
                                        break;
                                    }
                                }
                            }
                            WarningId += ")";

                            //теперь удалим их в outlook
                            cmd = new SqlCeCommand("select StoreID from Folders where Name='" + tableNameInOutlook + "' ", conn);
                            //выполняем селект
                            dr = cmd.ExecuteReader();
                            if (dr.Read())
                            {
                                string StoreFolder = dr["StoreID"].ToString();
                                dr.Close();
                                cmd.Dispose();

                                cmd = new SqlCeCommand("select EntryIDInOutlook,ID from " + syncTable.table + " where ID not in " + WarningId, conn);
                                //выполняем селект
                                dr = cmd.ExecuteReader();
                                while (dr.Read())
                                {
                                    //Удаляем по одной вакансии
                                    OnEventRemuveFild(new EventRemuveFildEventArgs(dr["EntryIDInOutlook"].ToString(), StoreFolder));
                                    //запишем в лог изменений ID  "на случай если упадём в exeption чтобы восстановить всё"    
                                    DeleteID.Add(dr["ID"].ToString());
                                }
                                dr.Close();
                                cmd.Dispose();
                                cmd = null;
                            }

                            //теперь простенький скюэль запрос на удаление из access
                            cmd = new SqlCeCommand("DELETE FROM  " + syncTable.table + " where ID not in " + WarningId, conn);
                            cmd.ExecuteNonQuery();
                            WarningId = null;
                            cmd.Dispose();
                            cmd = null;
                        }
                    }
                    if (Procent + 10 <= 100)
                        Procent += 10;


                    //-отчистим поле Modify 
                    cmd = new SqlCeCommand("UPDATE " + syncTable.table + " SET  Modify = '' where ID not in ('-1')", conn);
                    cmd.ExecuteNonQuery();
                    cmd.Dispose();
                    cmd = null;
                    GC.Collect();
                    GC.WaitForPendingFinalizers();

                    //обновим запись в таблице Sync 
                    if (action.UpdateChangeToken())
                        if (Procent < 100)
                            Procent = 100;
                }
                else
                {
                    Procent += 40;
                    //Записываем новые данные клиенту
                    DataRow[] drows = action.ds.Tables["Table"].Select("ID not in ('-1')");

                    OnEventCopyTable(new EventCopyTableEventArgs(drows, FolderOutlook));

                    Procent = 80;

                    //запишем в лог изменений ID  "на случай если упадём в exeption чтобы восстановить всё"    
                    foreach (DataRow dro in drows)
                        InsertID.Add(dro["ID"].ToString());

                    Procent = 90;

                    if (action.UpdateChangeToken())
                        if (Procent < 100)
                            Procent = 100;
                }
        }
        catch (Exception err)
        {
            if (err.Message.IndexOf("Unable to connect to the remote server") == -1)
            {
                if (err.Message.IndexOf("The remote name cold not be resolve:") != -1)
                    System.Windows.Forms.MessageBox.Show("Сообщение системному админестратору :\n В файле hosts не прописан URL \n The remote name cold not be resolve");
            }
            else
                System.Windows.Forms.MessageBox.Show("Клиенту не удалось подключиться к удалённому компьютеру \nВозможны проблемы \n - нет подключения к интернету \n - удалённый компьютер перегружен и не может принять сообщение клиента");

            Debug.Assert(false, "Synchronizer.cs UpdateTable()" + "\n" + err.Message + "\n -->" + err.Source + "\n"); 

            ret = false;
        }
    }
    catch (Exception ex)
    {
        Debug.Assert(false, "Synchronizer.cs UpdateTable()" + "\n" + ex.Message + "\n -->" + ex.Source + "\n"); 
    }
    finally
    {
        //закрываем подключение
        conn.Close();
        conn.Dispose();
        conn = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
        //А здесь надо сделать проверку контрольных сумм

    }
    return ret;
}
    
 
}

    #region тут определим классы параметров событий
    #region 2 События возникает до обновление записей которые у клиента не изменялись но на сервере изменились
    //1класс параметров передаваемых событиям
    internal class EventLoadUpdateEventArgs : EventArgs
    {
        private readonly DataRow m_drow;

        public EventLoadUpdateEventArgs(DataRow drow)
        {
            m_drow = drow;
        }

        public DataRow Drow { get { return m_drow; } }

    }

    //2 обновление данных в  oulook
    internal class EventOutlookUpdateEventArgs : EventArgs
    {
        private readonly DataRow m_drow;

        public EventOutlookUpdateEventArgs(DataRow drow)
        {
            m_drow = drow;
        }

        public DataRow Drow { get { return m_drow; } }

    }

    #endregion

    #region  Колизия типа изменений ->Вносити свои изменения 3 события
    //1 Событие до выбора варианта разрешения кализии
    internal class EventStartKolisy_1_EventArgs : EventArgs
    {
        private readonly SqlCeDataReader m_dr;

        public EventStartKolisy_1_EventArgs(SqlCeDataReader dr)
        {
            m_dr = dr;
        }

        public SqlCeDataReader Dr { get { return m_dr; } }

    }
    //3 Событие до изменения на стороне клиента (в случае нажатия НЕТ)
    internal class EventPosleIzmeneniaClientaKolisy_1_EventArgs : EventArgs
    {
        private readonly DataRow m_kolisi;

        public EventPosleIzmeneniaClientaKolisy_1_EventArgs(DataRow kolisi)
        {
            m_kolisi = kolisi;
        }

        public DataRow Kolisi { get { return m_kolisi; } }

    }
    #endregion

    #region Колизия Удаление вакансии Если удалена было на сервере до  последннего изменения то возникает колизия предлогаем пользователю  выбрать внести свои  изменения или оставить изменения которые на сервере
    //1 Событие до выбора варианта разрешения кализии
    internal class EventStartKolisy_2_EventArgs : EventArgs
    {
        private readonly SqlCeDataReader m_dr;
        private readonly Outlook.MAPIFolder m_folderVacancy;

        public EventStartKolisy_2_EventArgs(SqlCeDataReader dr, Outlook.MAPIFolder folderVacancy)
        {
            m_dr = dr;
            m_folderVacancy = folderVacancy;
        }

        public SqlCeDataReader Dr { get { return m_dr; } }
        public Outlook.MAPIFolder FolderVacancy { get { return m_folderVacancy; } }

    }
    //2 Событие после изменения на стороне Сервера 
    internal class EventPosleIzmeneniaServerKolisy_2_EventArgs : EventArgs
    {
        private readonly DataRow m_kolisi;

        public EventPosleIzmeneniaServerKolisy_2_EventArgs(DataRow kolisi)
        {
            m_kolisi = kolisi;
        }

        public DataRow Kolisi { get { return m_kolisi; } }

    }
    //3 Событие после изменения на стороне клиента 
    internal class EventPosleIzmeneniaClientaKolisy_2_EventArgs : EventArgs
    {
        private readonly DataRow[] m_kolisi;
        private readonly Outlook.MAPIFolder m_folderVacancy;

        public EventPosleIzmeneniaClientaKolisy_2_EventArgs(DataRow[] kolisi, Outlook.MAPIFolder folderVacancy)
        {
            m_kolisi = kolisi;
            m_folderVacancy = folderVacancy;
        }

        public DataRow[] Kolisi { get { return m_kolisi; } }
        public Outlook.MAPIFolder FolderVacancy { get { return m_folderVacancy; } }

    }

    #endregion

    #region Кализия Другой пользователь удалил вакансию после последней синхронизации.\n Создать на сервере Вакансию?"
    //1 Событие до выбора варианта разрешения кализии
    internal class EventStartKolisy_3_EventArgs : EventArgs
    {
        private readonly SqlCeDataReader m_dr;

        public EventStartKolisy_3_EventArgs(SqlCeDataReader dr)
        {
            m_dr = dr;
        }

        public SqlCeDataReader Dr { get { return m_dr; } }
    }
    #endregion


    //Событие до удаления записи из таблици клиента (По одной записи)
    internal class EventRemuveFildEventArgs : EventArgs
    {
        private readonly string m_entry, m_srore;
        public EventRemuveFildEventArgs(string entry, string srore)
        {
            m_entry = entry;
            m_srore = srore;
        }

        public string Entry { get { return m_entry; } }
        public string Srore { get { return m_srore; } }

    }

    //Вслучае первова запуска синхронизации таблици возникает это событие 
    //мы должны тупо скопировать тублицу с сервера
    internal class EventCopyTableEventArgs : EventArgs
    {
        private readonly DataRow[] m_dr;
        private readonly Outlook.MAPIFolder m_folderVacancy;

        public EventCopyTableEventArgs(DataRow[] dr, Outlook.MAPIFolder folderVacancy)
        {
            m_dr = dr;
            m_folderVacancy = folderVacancy;
        }

        public DataRow[] Dr { get { return m_dr; } }
        public Outlook.MAPIFolder FolderVacancy { get { return m_folderVacancy; } }

    }
    //Вот такое вот событие тут надо будит вносить данные
    //возмём все записи которые у клиента не обновлял  и не удалял ,and не те с котрыми клиент 
    //ничего не делал но уже скачал с сервера 
    internal class EventGetNewFildEventArgs : EventArgs
    {
        private readonly DataRow[] m_insering;
        private readonly Outlook.MAPIFolder m_folderVacancy;

        public EventGetNewFildEventArgs(DataRow[] insering, Outlook.MAPIFolder folderVacancy)
        {
            m_insering = insering;
            m_folderVacancy = folderVacancy;
        }

        public DataRow[] Insering { get { return m_insering; } }
        public Outlook.MAPIFolder FolderVacancy { get { return m_folderVacancy; } }

    }

    internal class ProcentEventArgs : EventArgs
    {
        private readonly int m_procent;

        public ProcentEventArgs(int procent)
        {
            m_procent = procent;
     
        }

        public int Procent { get { return m_procent; } }
    }

    internal class UpdateIDInOutlookEventArgs : EventArgs
    {
        private readonly string m_ID;
        private readonly string m_currentID;

        public UpdateIDInOutlookEventArgs(string id,string currentid)
        {
            m_ID = id;
            m_currentID = currentid;
        }

        public string ID 
        {
            get 
            { 
                return m_ID; 
            } 
        }

        public string CurrentID
        {
            get
            {
                return m_currentID;
            }
        }
    }


    #endregion



}
